package com.lhb.repertory.service.impl;

import cn.hutool.json.JSONUtil;
import com.lhb.repertory.model.*;
import com.lhb.repertory.service.SearchService;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.*;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 86131
 * @version 1.0
 * @description
 * @date 2025/8/8 下午2:31
 */
@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    RestHighLevelClient client;

    @Override
    public SearchFlightVO searchFlight(SearchFlightDTO searchFlightDTO) throws IOException {
        // 1.1创建Request
        SearchRequest request = new SearchRequest("repertory");
        // 1.2创建SearchSourceBuilder
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // 2.组织请求参数
        // 2.1.准备bool查询
        BoolQueryBuilder bool = QueryBuilders.boolQuery();

        bool.must(QueryBuilders.matchQuery("placeOfDeparture", searchFlightDTO.getPlaceOfDeparture()));
        bool.must(QueryBuilders.matchQuery("placeOfArrival", searchFlightDTO.getPlaceOfArrival()));

        bool.filter(QueryBuilders.termQuery("dateOfDeparture", searchFlightDTO.getDateOfDeparture()));
        if(searchFlightDTO.getPassengerType()!=3)
        bool.filter(QueryBuilders.termQuery("passengerType", searchFlightDTO.getPassengerType()));
        sourceBuilder.query(bool);
        //3.第一层 按航班号分组
        TermsAggregationBuilder flightNumberAgg = AggregationBuilders.terms("flightNumber_agg").field("flightNumber").size(100);
        //3.第二层 按舱位类型分组
        TermsAggregationBuilder cabinTypeAgg = AggregationBuilders.terms("cabinType_agg").field("cabinType").size(3);
        //3.第三层 每个舱位类型的最小值价格
        MinAggregationBuilder minAggregationBuilder = AggregationBuilders.min("min_price").field("price");
        //3.元数据采样
        TopHitsAggregationBuilder flightDataAgg = AggregationBuilders.topHits("flight_data").size(1);

        // 构建聚合
        TermsAggregationBuilder cabinAgg = AggregationBuilders.terms("cabin_agg")
                .field("cabinType")
                .size(3)
                .order(BucketOrder.key(true));  // 按舱位名称排序

        TopHitsAggregationBuilder ticketIdsAgg = AggregationBuilders.topHits("ticket_ids")
                .size(100)
                .fetchSource(false)
                .docValueField("id");

        cabinAgg.subAggregation(ticketIdsAgg);

        cabinTypeAgg.subAggregation(minAggregationBuilder);
        flightNumberAgg.subAggregation(cabinTypeAgg);
        flightNumberAgg.subAggregation(cabinAgg);
        flightNumberAgg.subAggregation(flightDataAgg);

        sourceBuilder.aggregation(flightNumberAgg);

        // 3. 添加聚合 - 获取航空公司、出发机场、目的机场列表
        sourceBuilder.aggregation(
                AggregationBuilders.terms("airline_agg").field("airline").size(100)
        );
        sourceBuilder.aggregation(
                AggregationBuilders.terms("airportOfDeparture_agg").field("airportOfDeparture").size(100)
        );
        sourceBuilder.aggregation(
                AggregationBuilders.terms("airportOfArrival_agg").field("airportOfArrival").size(100)
        );
        request.source(sourceBuilder);
        // 3.发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 4.解析响应
         return handleResponse(response);

    }

    @Override
    public List<SearchFlightVO> searchFlight2(SearchFlightDTO2 searchFlightDTO2) throws IOException {

        // 1.1创建Request
        SearchRequest request = new SearchRequest("repertory");
        // 1.2创建SearchSourceBuilder
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // 2.组织请求参数
        // 2.1.准备bool查询
        BoolQueryBuilder bool = QueryBuilders.boolQuery();

        bool.must(QueryBuilders.matchQuery("placeOfDeparture", searchFlightDTO2.getPlaceOfDeparture()));
        bool.must(QueryBuilders.matchQuery("placeOfArrival", searchFlightDTO2.getPlaceOfArrival()));

        bool.filter(QueryBuilders.termQuery("dateOfDeparture", searchFlightDTO2.getDateOfDeparture()));
        if(searchFlightDTO2.getPassengerType()!=3)
            bool.filter(QueryBuilders.termQuery("passengerType", searchFlightDTO2.getPassengerType()));
        sourceBuilder.query(bool);
        //3.第一层 按航班号分组
        TermsAggregationBuilder flightNumberAgg = AggregationBuilders.terms("flightNumber_agg").field("flightNumber").size(100);
        //3.第二层 按舱位类型分组
        TermsAggregationBuilder cabinTypeAgg = AggregationBuilders.terms("cabinType_agg").field("cabinType").size(3);
        //3.第三层 每个舱位类型的最小值价格
        MinAggregationBuilder minAggregationBuilder = AggregationBuilders.min("min_price").field("price");
        //3.元数据采样
        TopHitsAggregationBuilder flightDataAgg = AggregationBuilders.topHits("flight_data").size(1);

        // 构建聚合
        TermsAggregationBuilder cabinAgg = AggregationBuilders.terms("cabin_agg")
                .field("cabinType")
                .size(3)
                .order(BucketOrder.key(true));  // 按舱位名称排序

        TopHitsAggregationBuilder ticketIdsAgg = AggregationBuilders.topHits("ticket_ids")
                .size(100)
                .fetchSource(false)
                .docValueField("id");

        cabinAgg.subAggregation(ticketIdsAgg);


        cabinTypeAgg.subAggregation(minAggregationBuilder);
        flightNumberAgg.subAggregation(cabinTypeAgg);
        flightNumberAgg.subAggregation(cabinAgg);
        flightNumberAgg.subAggregation(flightDataAgg);


        sourceBuilder.aggregation(flightNumberAgg);

        // 3. 添加聚合 - 获取航空公司、出发机场、目的机场列表
        sourceBuilder.aggregation(
                AggregationBuilders.terms("airline_agg").field("airline").size(100)
        );
        sourceBuilder.aggregation(
                AggregationBuilders.terms("airportOfDeparture_agg").field("airportOfDeparture").size(100)
        );
        sourceBuilder.aggregation(
                AggregationBuilders.terms("airportOfArrival_agg").field("airportOfArrival").size(100)
        );
        request.source(sourceBuilder);
        // 3.发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 4.解析响应
        SearchFlightVO searchFlightVO1= handleResponse(response);

        // 1.1创建Request
        SearchRequest request2 = new SearchRequest("repertory");
        // 1.2创建SearchSourceBuilder
        SearchSourceBuilder sourceBuilder2 = new SearchSourceBuilder();
        // 2.组织请求参数
        // 2.1.准备bool查询
        BoolQueryBuilder bool2 = QueryBuilders.boolQuery();

        bool2.must(QueryBuilders.matchQuery("placeOfDeparture", searchFlightDTO2.getPlaceOfArrival()));
        bool2.must(QueryBuilders.matchQuery("placeOfArrival", searchFlightDTO2.getPlaceOfDeparture()));

        bool2.filter(QueryBuilders.termQuery("dateOfDeparture", searchFlightDTO2.getDateOfArrival()));
        if(searchFlightDTO2.getPassengerType()!=3)
            bool2.filter(QueryBuilders.termQuery("passengerType", searchFlightDTO2.getPassengerType()));
        sourceBuilder2.query(bool2);
        //3.第一层 按航班号分组
        TermsAggregationBuilder flightNumberAgg2 = AggregationBuilders.terms("flightNumber_agg").field("flightNumber").size(100);
        //3.第二层 按舱位类型分组
        TermsAggregationBuilder cabinTypeAgg2 = AggregationBuilders.terms("cabinType_agg").field("cabinType").size(3);
        //3.第三层 每个舱位类型的最小值价格
        MinAggregationBuilder minAggregationBuilder2 = AggregationBuilders.min("min_price").field("price");
        //3.元数据采样
        TopHitsAggregationBuilder flightDataAgg2 = AggregationBuilders.topHits("flight_data").size(1);

        // 构建聚合
        TermsAggregationBuilder cabinAgg2 = AggregationBuilders.terms("cabin_agg")
                .field("cabinType")
                .size(3)
                .order(BucketOrder.key(true));  // 按舱位名称排序

        TopHitsAggregationBuilder ticketIdsAgg2 = AggregationBuilders.topHits("ticket_ids")
                .size(100)
                .fetchSource(false)
                .docValueField("id");

        cabinAgg2.subAggregation(ticketIdsAgg2);

        cabinTypeAgg2.subAggregation(minAggregationBuilder2);
        flightNumberAgg2.subAggregation(cabinTypeAgg2);
        flightNumberAgg2.subAggregation(cabinAgg2);
        flightNumberAgg2.subAggregation(flightDataAgg2);

        sourceBuilder2.aggregation(flightNumberAgg2);

        // 3. 添加聚合 - 获取航空公司、出发机场、目的机场列表
        sourceBuilder2.aggregation(
                AggregationBuilders.terms("airline_agg").field("airline").size(100)
        );
        sourceBuilder2.aggregation(
                AggregationBuilders.terms("airportOfDeparture_agg").field("airportOfDeparture").size(100)
        );
        sourceBuilder2.aggregation(
                AggregationBuilders.terms("airportOfArrival_agg").field("airportOfArrival").size(100)
        );
        request2.source(sourceBuilder2);
        // 3.发送请求
        SearchResponse response2 = client.search(request2, RequestOptions.DEFAULT);
        // 4.解析响应
          SearchFlightVO searchFlightVO2= handleResponse(response2);

        ArrayList<SearchFlightVO> searchFlightVOS = new ArrayList<>();
        searchFlightVOS.add(searchFlightVO1);
        searchFlightVOS.add(searchFlightVO2);
        return searchFlightVOS;

    }

    @Override
    public List<SearchFlightVO> searchFlight3(List<SearchFlightDTO> searchFlightDTOS) throws IOException {
        ArrayList<SearchFlightVO> searchFlightVOS = new ArrayList<>();
        for (SearchFlightDTO searchFlightDTO : searchFlightDTOS) {
            SearchFlightVO searchFlightVO = searchFlight(searchFlightDTO);
            searchFlightVOS.add(searchFlightVO);
        }
        return  searchFlightVOS;
    }


    private SearchFlightVO handleResponse(SearchResponse response) {
        SearchFlightVO result = new SearchFlightVO();

       // 1.获得按航班号的舱位分类票数和元数据
        result.setFlightVOS(parseAggregationResponse(response));

        // 2. 处理聚合结果
        Aggregations aggregations = response.getAggregations();

        // 航空公司列表
        List<String> airlineNames = extractAggregation(aggregations, "airline_agg");
        result.setAirlineNames(airlineNames);

        // 出发机场列表
        List<String> departureAirportNames = extractAggregation(aggregations, "airportOfDeparture_agg");
        result.setDepartureAirportNames(departureAirportNames);

        // 目的机场列表
        List<String> arrivalAirportNames = extractAggregation(aggregations, "airportOfArrival_agg");
        result.setArrivalAirportNames(arrivalAirportNames);

        return result;
    }

    // 辅助方法：从聚合结果中提取字符串列表
    private List<String> extractAggregation(Aggregations aggregations, String aggName) {
        List<String> result = new ArrayList<>();

        if (aggregations != null) {
            Terms terms = aggregations.get(aggName);
            if (terms != null) {
                for (Terms.Bucket bucket : terms.getBuckets()) {
                    result.add(bucket.getKeyAsString());

                }
            }
        }

        return result;
    }


    private List<FlightVO> parseAggregationResponse(SearchResponse response) {
        List<FlightVO> result = new ArrayList<>();

        // 获取第一层聚合（航班号分组）
        Terms flightsAgg = response.getAggregations().get("flightNumber_agg");

        for (Terms.Bucket flightBucket : flightsAgg.getBuckets()) {

            FlightVO flightVO=new FlightVO();
            // 获取舱位统计子聚合
            Terms cabinStatsAgg = flightBucket.getAggregations().get("cabinType_agg");
            List<Cabin>cabins=new ArrayList<>();
            for (Terms.Bucket cabinBucket : cabinStatsAgg.getBuckets()) {
                String cabinClass = cabinBucket.getKeyAsString();
                long docCount = cabinBucket.getDocCount();
                Min min_price = cabinBucket.getAggregations().get("min_price");
                Integer price= (int) min_price.getValue();
                Cabin cabin = new Cabin(cabinClass, docCount,price);
                cabins.add(cabin);

            }
            flightVO.setCabins(cabins);

            // 处理结果
            List<String> economyTickets = new ArrayList<>();
            List<String> businessTickets = new ArrayList<>();
            List<String> firstClassTickets = new ArrayList<>();

            Terms cabinTerms = flightBucket.getAggregations().get("cabin_agg");
            for (Terms.Bucket bucket : cabinTerms.getBuckets()) {
                String cabinType = bucket.getKeyAsString();
                TopHits hits = bucket.getAggregations().get("ticket_ids");

                for (SearchHit hit : hits.getHits().getHits()) {
                    String ticketId = hit.getId();
                    switch(cabinType) {
                        case "经济舱": economyTickets.add(ticketId); break;
                        case "公务舱": businessTickets.add(ticketId); break;
                        case "头等舱": firstClassTickets.add(ticketId); break;
                    }
                }
            }
            flightVO.setEconomyTickets(economyTickets);
            flightVO.setBusinessTickets(businessTickets);
            flightVO.setFirstClassTickets(firstClassTickets);

            // 获取元数据采样
            TopHits sampleData = flightBucket.getAggregations().get("flight_data");
            if (sampleData.getHits().getHits().length > 0) {
                String source = sampleData.getHits().getHits()[0].getSourceAsString();

                FlightDoc flightDoc = JSONUtil.toBean(source, FlightDoc.class);


                flightVO.setFlightDoc(flightDoc);
            }

            result.add(flightVO);
        }

        return result;
    }
}
